﻿using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using FitNesseRunner.Model;

namespace FitNesseRunner
{
	public class PageParser
	{
		private readonly IPageObserver _pageObserver;

		public PageParser(IPageObserver pageObserver)
		{
			_pageObserver = pageObserver;
		}

		private const string TableContentGroupName = "content";
		private const string EscapedTableGroupName = "escaped";
		private const string TableLinePattern = @"^(?<" + EscapedTableGroupName + @">!?)\|((?<" + TableContentGroupName + @">[^\|]*)\|)+\s*$";

		public void Parse(IEnumerable<string> lines)
		{
			var tableRows = new List<IEnumerable<string>>();
			foreach (var line in lines)
			{
				if ((!IsTableLine(line) || IsEscapedTableLine(line)) && tableRows.Count > 0)
				{
					CreateTableElement(tableRows);
					tableRows = new List<IEnumerable<string>>();
				}

				if (IsTableLine(line))
					tableRows.Add(GetTextFromTableLine(line));
				else if (line.StartsWith("!"))
					CreateDirectiveElement(line);
			}

			if (tableRows.Count == 0)
				return;
			
			CreateTableElement(tableRows);
		}

		private void CreateDirectiveElement(string line)
		{
			var directiveElement = new Directive(line);
			_pageObserver.HandleDirective(directiveElement);
		}

		private void CreateTableElement(IEnumerable<IEnumerable<string>> tableRows)
		{
			var tableRowsWithoutMarkup = HandleEscapedContent(tableRows);
			var tableElement = new Table(tableRowsWithoutMarkup);
			_pageObserver.HandleTable(tableElement);
		}

		private static IEnumerable<IEnumerable<string>> HandleEscapedContent(IEnumerable<IEnumerable<string>> tableRows)
		{
			return from tableRow in tableRows
			       select (from cell in tableRow
			               select ExtractEscapedContent(cell));
		}

		private static string ExtractEscapedContent(string cell)
		{
			if (cell.StartsWith("!-") && cell.EndsWith("-!"))
				return cell.Substring(2, cell.Length - 4);

			return cell;
		}

		private static bool IsEscapedTableLine(string line)
		{
			return Regex.Match(line, TableLinePattern).Groups[EscapedTableGroupName].Value != string.Empty;
		}

		private static IEnumerable<string> GetTextFromTableLine(string line)
		{
			var match = Regex.Match(line, TableLinePattern);
			return from Capture capture in match.Groups[TableContentGroupName].Captures
			       select capture.Value;
		}

		private static bool IsTableLine(string line)
		{
			return Regex.IsMatch(line, TableLinePattern);
		}

		private class Directive : IDirective
		{
			public Directive(string line)
			{
				const string pattern = @"^\!(?<name>[\w\d]+)(\s(?<args>[\w\W]*))?";
				var match = Regex.Match(line, pattern);
				Name = match.Groups["name"].Value;
				Arguments = match.Groups["args"].Value;
			}

			public string Name { get; private set; }
			public string Arguments { get; private set; }
		}

		private class Table : ITable
		{
			private readonly IEnumerable<IEnumerable<string>> _cellContent;

			public Table(IEnumerable<IEnumerable<string>> cellContent)
			{
				_cellContent = cellContent;
			}

			public IEnumerable<IEnumerable<string>> Cells
			{
				get { return _cellContent; }
			}
		}
	}
}